package com.anjubao.parking.municipal.parkinglot.service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSONObject;
import com.anjubao.parking.municipal.common.dto.ApiCode;
import com.anjubao.parking.municipal.common.dto.ApiResult;
import com.anjubao.parking.municipal.common.exception.ServiceException;
import com.anjubao.parking.municipal.common.global.BusinessCodeEnum;
import com.anjubao.parking.municipal.common.utils.*;
import com.anjubao.parking.municipal.parkinglot.constant.ParkingStatusEnum;
import com.anjubao.parking.municipal.parkinglot.model.dto.ParkingLotBillRuleModelDto;
import com.anjubao.parking.municipal.parkinglot.dto.ParkingLotDto;
import com.anjubao.parking.municipal.parkinglot.dto.ParkingLotStatDto;
import com.anjubao.parking.municipal.parkinglot.form.AreaScreeningParkingLotForm;
import com.anjubao.parking.municipal.parkinglot.form.PositionScreeningParkingLotForm;
import com.anjubao.parking.municipal.parkinglot.mapper.ParkingLotBillRuleMapper;
import com.anjubao.parking.municipal.parkinglot.mapper.ParkingLotBillRuleRelateMapper;
import com.anjubao.parking.municipal.parkinglot.model.ParkingLotBillRule;
import com.anjubao.parking.municipal.parkinglot.model.ParkingLotBillRuleRelate;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.anjubao.parking.municipal.parkinglot.mapper.ParkingLotMapper;
import com.anjubao.parking.municipal.parkinglot.model.ParkingLot;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * 
 * ParkingLot服务实现类
 * 
 * @author chenfang
 *
 */
@Service
@Slf4j
public class ParkingLotService extends ServiceImpl<ParkingLotMapper, ParkingLot> {

	@Autowired
	private ParkingLotMapper parkingLotRepo;

	@Autowired
	private RestTemplate restTemplate;
	@Resource
	private ParkingLotBillRuleService parkingLotBillRuleService;
	@Resource
	private ParkingLotBillRuleRelateService parkingLotBillRuleRelateService;


	@Value("${ParkingLots.less:0.3}")
	//车位不足
	private String parkingLotsLessPercent;


	@Value("${ParkingLots.full:0.1}")
	//车位已满
	private String parkingLotsFullPercent;

	@Value("${roadSideParkingIp:http://192.168.41.147:9003/roadsideParking}")
	//车位已满
	private String roadSideIp;
	@Resource
	private ParkingLotBillRuleRelateMapper parkingLotBillRuleRelateMapper;
	@Resource
	private ParkingLotBillRuleMapper parkingLotBillRuleMapper;

	@Transactional(rollbackFor = Exception.class)
	public ParkingLot saveParkingLot(ParkingLot parkingLot,List<String> billRuleCodes) {
		log.debug("saving parking lot, {}", parkingLot);
		if(StringUtils.isEmpty(parkingLot.getCode())){//判断是否有code
			parkingLot.setId(U.id());
			parkingLot.setCode(MunicipalParkingUtils.generateCode(parkingLot.getId(), BusinessCodeEnum.PARKING_CODE.getCode()));

			if(parkingLot.getType()==2) {
				String url = roadSideIp + "/sync/lot/1";
				JSONObject object = new JSONObject();
				object.put("id", parkingLot.getId());
				object.put("code", parkingLot.getCode());
				object.put("parkName", parkingLot.getName());
				object.put("address", parkingLot.getLocation());
				ApiResult<String> res = this.restTemplate.postForObject(url, object, ApiResult.class);
				if (ApiCode.STATUS_OK.getCode() ==res.getCode()){
                    saveOrUpdate(parkingLot);

                    /*新增计费规则*/
                    if (CollectionUtils.isEmpty(billRuleCodes)) {
                        throw new ServiceException("必须选择停车收费规则");
                    }
                    this.buildAndSaveBillRuleRelate(billRuleCodes, parkingLot.getId(), parkingLot.getName());
				}else {
					throw new ServiceException("车场添加失败,请重试!") ;
				}
			}

			return parkingLot;

		}else{
			parkingLot.setId(MunicipalParkingUtils.getIdByCode(parkingLot.getCode()));

			if(parkingLot.getType()==2){
				String url=roadSideIp+"/sync/lot/2";
				JSONObject object=new JSONObject();
				object.put("id",parkingLot.getId());
				object.put("code",parkingLot.getCode());
				object.put("parkName",parkingLot.getName());
				object.put("address",parkingLot.getLocation());
				ApiResult<String> res= this.restTemplate.postForObject(url,object, ApiResult.class);
				if (ApiCode.STATUS_OK.getCode() ==res.getCode()){
					saveOrUpdate(parkingLot);

					/*新增计费规则*/
					if (!CollectionUtils.isEmpty(billRuleCodes)) {
						//删除旧的关联关系
						parkingLotBillRuleRelateMapper.delete(new UpdateWrapper<>(new ParkingLotBillRuleRelate().setParkingLotId(parkingLot.getId())));
						this.buildAndSaveBillRuleRelate(billRuleCodes, parkingLot.getId(), parkingLot.getName());
					}
				}else {
					throw new ServiceException("车场更新失败,请重试!") ;
				}
			}

			return parkingLot;
		}


	}

	//创建计费规则关联关系
	private void buildAndSaveBillRuleRelate(List<String> billRuleCodes, Long parkingLotId, String parkingLotName) {
		for (String billRuleCode : billRuleCodes) {
			ParkingLotBillRule billRule = parkingLotBillRuleMapper.selectById(MunicipalParkingUtils.getIdByCode(billRuleCode));
			if (null == billRule) {
				throw new ServiceException("无此计费规则 code:"+billRuleCode);
			}
			ParkingLotBillRuleRelate parkingLotBillRuleRelate = new ParkingLotBillRuleRelate();
			parkingLotBillRuleRelate.setId(U.id());
			parkingLotBillRuleRelate.setCode(MunicipalParkingUtils.generateCode(parkingLotBillRuleRelate.getId(),BusinessCodeEnum.PAY_BILL_RULE_CODE.getCode()));
			parkingLotBillRuleRelate.setParkingLotId(parkingLotId);
			parkingLotBillRuleRelate.setParkingLotName(parkingLotName);
			parkingLotBillRuleRelate.setParkingLotBillRuleId(billRule.getId());
			parkingLotBillRuleRelate.setParkingLotBillRuleName(billRule.getName());
			parkingLotBillRuleRelate.setNote("");
			parkingLotBillRuleRelateMapper.insert(parkingLotBillRuleRelate);
		}
	}


	public Optional<ParkingLot> findById(long id) {
		return Optional.ofNullable(parkingLotRepo.selectById(id));
	}

	public void delete(ParkingLot entity) {
//		String url=roadSideIp+"/sync/lot/"++"/delete";
		parkingLotRepo.deleteById(entity.getId());
	}

	public IPage<ParkingLot> positionScreeningParkingLot(Page<ParkingLot> page, PositionScreeningParkingLotForm form) {
		return parkingLotRepo.positionScreeningParkingLot(page,form);
	}

	public IPage<ParkingLotDto> areaScreeningParkingLot(Page<ParkingLotDto> page, AreaScreeningParkingLotForm form) {
		if(form.getParkingStatus()!=null){
			if(ParkingStatusEnum.ParkingLotsLess.getCode()==form.getParkingStatus()){
				return parkingLotRepo.areaScreeningParkingLot(page,form,parkingLotsFullPercent,parkingLotsLessPercent);
			}else if(ParkingStatusEnum.ParkingLotsMore.getCode()==form.getParkingStatus()){
				return parkingLotRepo.areaScreeningParkingLot(page,form,parkingLotsLessPercent,"1");
			}else if(ParkingStatusEnum.ParkingLotsFull.getCode()==form.getParkingStatus()){
				return parkingLotRepo.areaScreeningParkingLot(page,form,"0",parkingLotsFullPercent);
			}
		}
		IPage<ParkingLotDto> res= parkingLotRepo.areaScreeningParkingLot(page,form,null,null);
		if(res.getTotal()!=0){
			for(ParkingLotDto dto:res.getRecords()){
				Float freeOfTotal= Float.valueOf(dto.getFreeSlots())/Float.valueOf(dto.getTotalSlots());
				if(freeOfTotal<Float.valueOf(parkingLotsFullPercent)){
					dto.setParkingStatus(ParkingStatusEnum.ParkingLotsFull.getCode());
				}else  if(freeOfTotal>Float.valueOf(parkingLotsLessPercent)){
					dto.setParkingStatus(ParkingStatusEnum.ParkingLotsMore.getCode());
				}else {
					dto.setParkingStatus(ParkingStatusEnum.ParkingLotsLess.getCode());
				}
			}
		}
		return  res;
	}

    public List<ParkingLotStatDto> parkingLotStatistical(AreaScreeningParkingLotForm form) {
        return parkingLotRepo.parkingLotStatistical(form);
    }


	/**
	 * 通过经纬度找停车场
	 */
	public List<ParkingLotDto> findParkingLot(Double latitude, Double longitude) {

		//查询所有停车场信息
		List<ParkingLot> parkingLots = parkingLotRepo.selectList(null);
		//对象转换
		return parkingLots2Dtos(parkingLots,latitude,longitude);

	}

	/**
	 * 通过名称/位置找车场
	 * @param name 车场名称/位置
	 */
	public List<ParkingLotDto> searchParkingLot(String name,Double latitude,Double longitude) {

		List<ParkingLot> parkingLots =  parkingLotRepo.selectList(new QueryWrapper<>(
				new ParkingLot()).lambda().like(StringUtils.isNotBlank(name), ParkingLot::getLocation,name).or().like(StringUtils.isNotBlank(name),ParkingLot::getName,name)
		);
		return parkingLots2Dtos(parkingLots,latitude,longitude);
	}

	private List<ParkingLotDto> parkingLots2Dtos(List<ParkingLot> parkingLots,Double latitude,Double longitude){

		Set<Long> parkingLotIds = parkingLots.stream().map(ParkingLot::getId).collect(Collectors.toSet());

		//停车场id->计费规则列表
		Map<Long, List<ParkingLotBillRuleModelDto>> billRuleByParkingLotIds = parkingLotBillRuleService.getBillRuleByParkingLotIds(parkingLotIds);
		log.info("parkingLotIds:{} {}",U.json(parkingLotIds),U.json(billRuleByParkingLotIds));

		return parkingLots.stream().map(t -> {
			ParkingLotDto transform = BeanUtils.transform(ParkingLotDto.class, t);
			transform.setParkingLotCode(t.getCode());
			//增加计费描述
			ParkingLotBillRuleModelDto modelDto = billRuleByParkingLotIds.get(t.getId()).get(0);
			transform.setDayFeeSection(modelDto.getBillRuleSub().getDayFeeSection());
			transform.setDayTimeSection(modelDto.getBillRuleSub().getDayTimeSection());
			//增加距离
			double distance = getDistance(longitude, latitude, t.getLongitude(), t.getLatitude());
			transform.setDistance(distance);
			return transform;

		}).collect(Collectors.toList());

	}

	/**
	 * 计算两经纬度点之间的距离（单位：米）
	 */
	private static double getDistance(double lng1,double lat1,double lng2,double lat2){
		double radLat1 = Math.toRadians(lat1);
		double radLat2 = Math.toRadians(lat2);
		double a = radLat1 - radLat2;
		double b = Math.toRadians(lng1) - Math.toRadians(lng2);
		double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1)
				* Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
		s = s * 6378137.0;// 取WGS84标准参考椭球中的地球长半径(单位:m)
		s = Math.round(s * 10000) / 10000;
		return s;
	}

}
